有位友人想要點餐,他問能否可以有個 command line 的方式來上傳 ibon 檔案,雖然我不知道他會怎麼使用它,但其實能幫人家省時間就是一件有價值的事情,那我們今天的主題就來挑戰用 ibon 自動上傳檔案吧。
iBon 上傳的網址是 https://www.ibon.com.tw/mobile/printscan/D0111_fileupload_innerifrm.aspx ,點進去以後,能看到要填入的欄位不會很多,主要是姓名、Email、上傳文件和一個 checkbox。輸入完畢後按「確認上傳」,這邊可以看到接下來會送出一個 Content-Type:multipart/form-data;
的 post request,裡面的 post form data 有 __VIEWSTATE
、__EVENTVALIDATION
、hsTime
、txtUserName
、txtEmail
、fuFile
、chkboxAgree
、lnkbtnUpload
。
裡面大概最有技術難度的是 __VIEWSTATE
、__EVENTVALIDATION
,之前的「台彩的銷售地點」有提到,這是 asp.net 的一種狀態的驗證機制,所以我們必須模擬出這兩個數值出來,才能正確地送出。
接下來我們觀察到,post request 的 response 並沒有內容,而整個流程再送出之後,主動的再發出一個 get request,而我們所需要的取件編號,就在這個 request 裡面。
我們若要上傳檔案,那麼會拆解成以下兩個步驟:
那我們就先來模擬一下我們所送出的 post request,將 url、method、header、form data 輸入進 postman,送出後是有回傳 200。
接下來我們在模擬發一個 get request 來取得取件編號,這邊有成功取得,整個過程應該就沒問題了。
之前我們的做法是直接使用已經得到的 __VIEWSTATE
、__EVENTVALIDATION
,這次我們不要使用已經找出來的,我們使用網頁來模擬產出,所以在先模擬人工走到上傳頁面,取得 __VIEWSTATE
、__EVENTVALIDATION
。
request('https://www.ibon.com.tw/mobile/printscan/D0111_fileupload_innerifrm.aspx', (err, res, body)=>{
var $ = cheerio.load(body)
var __VIEWSTATE = $('#__VIEWSTATE').val()
var __EVENTVALIDATION = $('#__EVENTVALIDATION').val()
})
接下來我們就來實作 post request
request('https://www.ibon.com.tw/mobile/printscan/D0111_fileupload_innerifrm.aspx', (err, res, body)=>{
var $ = cheerio.load(body)
var __VIEWSTATE = $('#__VIEWSTATE').val()
var __EVENTVALIDATION = $('#__EVENTVALIDATION').val()
var options = {
url: 'https://www.ibon.com.tw/mobile/printscan/D0111_fileupload_innerifrm.aspx',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
formData: {
'fuFile': fs.createReadStream(__dirname + '/test.pdf'),
'__VIEWSTATE': __VIEWSTATE,
'__EVENTVALIDATION': __EVENTVALIDATION,
'txtUserName': 'Howard',
'chkboxAgree': 'on',
'lnkbtnUpload': '確認上傳',
'txtEmail': 'tnstiger@gmail.com',
}
}
request(options, (err, res, body)=>{
})
})
最後我們再發個 get request 來取得編號就可以了。
request('https://www.ibon.com.tw/mobile/printscan/D0111_fileupload_info.aspx', (err, res, body)=>{
var $ = cheerio.load(body)
console.log($('td span.valignmiddle').text().trim());
})
const request = require('request').defaults({jar: true});
const cheerio = require('cheerio');
const fs = require('fs');
request('https://www.ibon.com.tw/mobile/printscan/D0111_fileupload_innerifrm.aspx', (err, res, body)=>{
var $ = cheerio.load(body)
var __VIEWSTATE = $('#__VIEWSTATE').val()
var __EVENTVALIDATION = $('#__EVENTVALIDATION').val()
var options = {
url: 'https://www.ibon.com.tw/mobile/printscan/D0111_fileupload_innerifrm.aspx',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
formData: {
'fuFile': fs.createReadStream(__dirname + '/test.pdf'),
'__VIEWSTATE': __VIEWSTATE,
'__EVENTVALIDATION': __EVENTVALIDATION,
'txtUserName': 'Howard',
'chkboxAgree': 'on',
'lnkbtnUpload': '確認上傳',
'txtEmail': 'tnstiger@gmail.com',
}
}
request(options, (err, res, body)=>{
request('https://www.ibon.com.tw/mobile/printscan/D0111_fileupload_info.aspx', (err, res, body)=>{
var $ = cheerio.load(body)
console.log($('td span.valignmiddle').text().trim());
})
})
})
基本上在網頁上的任何行為都是有機會被自動化的,尤其是遇到批量的時候,更能幫我們省下很多寶貴的時間。
這次的主題,其實可以再加上個自動產出報表和 cron 排成上傳,這樣就能夠讓公司業務可以隨時地在 7-11 列印出今天的行程或報表。爬蟲和自動化並不是單一動作,組合技會更有價值。